import sys
import os
import stat
import answer
import condition
import conditions
import time
import operator
from datetime import tzinfo, timedelta, datetime, date
from time import strftime
from string import Template
from DatabaseConnector import connection

#Build the dictionary of synonyms to corresponding terms from a specific file
#File format is [term#]: [synonym1]; [synonym2]; [synonym3] ... ...
def buildHash(filePath):
    try:
        input = open(filePath,'r')
        s = input.readlines()
        dictionary = {}

        for line in s:
            if line.strip()[0] <> "#":
                term = line.split(':')[0].strip().lower()
                synonyms = line.split(':')[1].strip().lower()
                dictionary[term] = synonyms
#        print dictionary
        return dictionary                

    except IOError, (errno, strerror):
        print "I/O error(%s): %s" % (errno, strerror)
        
#For building hash dictionary for snomed findings
def buildFindingsHash(filePath):
    try:
        input = open(filePath,'r')
        s = input.readlines()
        dictionary = {}

        for line in s:
            colon_index = line.find(':')
            concept_id = line[0:colon_index].strip().lower()
            synonyms = line[colon_index+1:len(line)].split(';')
            for syn in synonyms:
                syn = syn.strip().lower().replace("[semi]", ";")
                dictionary[syn] = concept_id

        #print dictionary
        return dictionary                

    except IOError, (errno, strerror):
        print "I/O error(%s): %s" % (errno, strerror)
        
#Format output for an automatically generated file to 'output.txt'
#File format is [term]: 
def convertFile(filePath):
    try:
        input_file = open(filePath,'r')
        output_file = open('output.txt', 'w')
        s = input_file.readlines()

        for line in s:
            term = line.split('***')[0].strip().lower()
            term += ':\n'
            output_file.write(term)

    except IOError, (errno, strerror):
        print "I/O error(%s): %s" % (errno, strerror)

#Build the array of categoric terms from a specific file
def buildCategoricArray(filePath):
    try:
        input = open(filePath, 'r')
        s = input.readlines()
        categoricArray = []

        for line in s:
            term = line.strip().lower()
            categoricArray.append(term)

        return categoricArray
    
    except IOError, (errno, strerror):
        print "I/O error(%s): %s" % (errno, strerror)

#Build the array of categoric terms from a specific file
def buildArray(filePath):
    try:
        input = open(filePath, 'r')
        s = input.readlines()
        array = []

        for line in s:
            term = line.strip().lower()
            array.append(term)

        return array
    
    except IOError, (errno, strerror):
        print "I/O error(%s): %s" % (errno, strerror)
        
def getAnswers(answerString, ref_fns, stat_fns, var_dictionary):
    answers = [] #array of answers
    
    #split the answers seperated by the commas into an array namely 'answers'
    answer_parts = answerString.split(',')
    for answer_part in answer_parts:
        index = answer_part.find(' of ')
#        print answer_part
        ref_fn_array = [] #array of ref_fns
        stat_fn_array = [] #array of stat_fns
        if index != -1:
            fn_str = answer_part[0:index].strip() # function string before "OF"
            variable = answer_part[index + 4:len(answer_part)].strip()
            variable = getTerm(variable, var_dictionary)           

            if variable is not None:   

                fn_parts = fn_str.split(' and ') # split string using 'and'
                for fn_part in fn_parts:
                    ref_index = inList(ref_fns, fn_part)
                    if ref_index != -1:
                        ref_fn_array.append(fn_part)
                    stat_index = inList(stat_fns, fn_part)
                    if stat_index != -1:
                        stat_fn_array.append(fn_part)

                #create a new answer object
                ans = answer.answer(variable, ref_fn_array, stat_fn_array)

                #append answer object to array
                answers.append(ans)
            
        '''
        else:
            variable = answer_part.strip()
            variable = getTerm(variable, var_dictionary)
            
            #create a new answer object
            ans = answer.answer(variable)

            #append answer object to array
            answers.append(ans)
        '''
#    print answers.variable
    return answers


def getConditions(conditionString, comparands, conditionTypes, att_dictionary):
    current = 0 # current index
    conditions = [] # array of conditions
    conditionType = ""
    firstflag = 0

    if conditionString == "" or conditionString is None:
        return None

    # get the index of the first comparand
    index = getIndex(conditionString, comparands, current)
    
    while index != -1:
        # get the trimmed attribute (immediately before comparand)
        attribute = conditionString[current:index].strip()
        attribute = getTerm(attribute, att_dictionary)
        current = index

        # get the comparand (>, <, =, etc)
        comparand = str(getComparand(conditionString, index))
        compLen = len(comparand)

        # get the trimmed value (immediately after comparand)
        index = getIndex(conditionString, conditionTypes, current)
        if index == -1:
            value = conditionString[current + compLen:len(conditionString)].strip()
            cond = condition.condition(" ", attribute, comparand, value)
                # Append cond to array
            conditions.append(cond)
            break
        else:
            value = conditionString[current + compLen:index].strip()
        
            # get the condition type for the next condition, if any (AND, OR)
#            print conditionType
            conditionType = str(getConditionType(conditionString, index))
                        
            # Create a new Condition object if attribute is defined
            if attribute is not None:
                cond = condition.condition(" " + conditionType + " ", attribute, comparand, value)
                # Append cond to array
                conditions.append(cond)
                
            typeLen = len(conditionType)

            current = index + typeLen

            # get the index of the next comparand
            if index != -1:
                index = getIndex(conditionString, comparands, current)
    conditions_ = SpecialProcessing(conditions)
    return conditions_

# return the index of a string containing any one of the keys, starting at start
def getIndex(string, keys, start):
    indices = []
    for i in range(0,len(keys)):
#        print string.lower()
#        print keys[i].lower()
#        print start
        index = string.lower().find(keys[i].lower(), start)
        if index >= 0:
            indices.append(index)
    if len(indices) == 0:
        return -1
    else:
        indices.sort()
        return indices[0]

# return the key if 'string' contains it, giving the index of the string (got from getIndex())
def getKey(string, keys, index):
	for key in keys:
		if string[index:len(string)].lower().startswith(key.lower()):
			return key
	
	return -1
	
# return the comparand of a string given the index to '>' or '<'
def getComparand(string, index):
    if string[index] == '>':
    	if index == len(string) - 1:
    		return '>'
        elif string[index+1] == '=':
            return ">="
        else:
            return ">"
    elif string[index] == '<':
    	if index == len(string) - 1:
    		return '<'
        elif string[index+1] == '=':
            return "<="
        elif string[index+1] == '>':
            return "<>"
        else:
            return "<"
    elif string[index] == '=':
        return '='

# return the condition type given the index to 'AND' or 'OR'
def getConditionType(string, index):
    if string[index].lower() == 'a':
        return "and"
    elif string[index].lower() == 'o':
        return "or"

# return the corresponding database term for user input
def getTerm(string, dictionary):
    return dictionary.get(string.lower(), None)

# return the number of hours according to the time_entity
def getHours(time_entity, time_value):
    if time_entity == 'H':
        return int(time_value)
    elif time_entity == 'D':
        return int(time_value) * 24
    elif time_entity == 'W':
        return int(time_value) * 24 * 7
    elif time_entity == 'Y':
        return int(time_value) * 24 * 365
    else:
        return -1
    
# return the index of the keyword in a list
def inList(list_, key):
    index = 0
    for value in list_:
        if value == key:
	    return index
        else:
	    index = index + 1
    return -1

# subTime subtracts current time 'now' by num_ent tim_ent (E.g. 48 "hours")
def subTime(now, num_ent, tim_ent):
    ##############changed 10/10#################
    #############default is hours###############
    interval = datetime.timedelta(hours = num_ent)
    if tim_ent == "days":
	interval = datetime.timedelta(days = num_ent)
    elif tim_ent == "weeks":
	interval = datetime.timedelta(weeks = num_ent)
    then = now - interval
    #print then.strftime("%Y %m %d %I")
    return then

# calculates the number of hours in num_ent time_ent (E.g. 2 days = 48 hours)
def toHours(num_ent, tim_ent):
    if tim_ent == "hours":
        return num_ent
    elif tim_ent == "days":
        return 24 * num_ent
    elif tim_ent == "weeks":
        return 7 * 24 * num_ent
    else:
        return -1

# obtains the time_entity (year, month, day, hour) of time
# E.g. time = 2007-1-15-09:24:30, getTimeEnt(time,"day") returns 15
def getTimeEnt(time, tim_ent):
    if tim_ent == "year":
        return time.strftime("%Y")
    elif tim_ent == "month":
        return time.strftime("%m")
    elif tim_ent == "day":
        return time.strftime("%d")
    elif tim_ent == "hour":
        return time.strftime("%H")
    elif tim_ent == "12hour":
        return time.strftime("%I")
    else:
        return -1

# converts the format used by real-time database
# E.g. 2007-07-27 20:00:00
def toRealTime(year, month, day, hour):
    if month < 10:
        mon = Template('0$mm').substitute(mm=month)
    if month > 9:
        mon = Template('$mm').substitute(mm=month)
    if day < 10:
        da = Template('0$dd').substitute(dd=day)
    if day > 9:
        da = Template('$dd').substitute(dd=day)
    if hour < 10:
        hr = Template('0$hh').substitute(hh=hour)
    if hour > 9:
        hr = Template('$hh').substitute(hh=hour)
    time = Template('$y-$m-$d $h:00:00').substitute(y=year, m=mon, d=da, h=hr)

    return time

# converts the format used by archive-time database
# E.g. 2007-07-27-20
def toArchiveTime(year, month, day, hour):
    if month < 10:
        mon = Template('0$mm').substitute(mm=month)
    if month > 9:
        mon = Template('$mm').substitute(mm=month)
    if day < 10:
        da = Template('0$dd').substitute(dd=day)
    if day > 9:
        da = Template('$dd').substitute(dd=day)
    if hour < 10:
        hr = Template('0$hh').substitute(hh=hour)
    if hour > 9:
        hr = Template('$hh').substitute(hh=hour)

    time = Template('$y-$m-$d-$h').substitute(y=year, m=mon, d=da, h=hr)

    return time

# combine two result sets into a single result set according to the logical operator
def combine(array1, array2, logical_operator):
    result_array = []
    if logical_operator.lower() == 'and':
        for value1 in array1:
            for value2 in array2:
                if value1 == value2:
                    result_array.append(value1)

    if logical_operator.lower() == 'or':
        for value1 in array1:
            result_array.append(value1)
            
        for value2 in array2:
            appeared = 0
            for value1 in array1:
                if value2 == value1:
                    appeared = 1

            if appeared == 0:
                result_array.append(value2)
            
    return result_array           

# return a list of final results from an array of results and corresponding logical operators, intersects with the correct patients' demographical info
def getFinalResult(patients, resultsArray, logicalArray):
    results = []

    if len(resultsArray) == 0:
        return patients
    elif len(resultsArray) == 1:
        results = resultsArray[0]
    else:
        results = combine(resultsArray[0], resultsArray[1], logicalArray[1])
        i = 2
        while i != len(resultsArray):
            results = combine(results, resultsArray[i], logicalArray[i])
            i = i + 1

    if patients is None:
        return results
    
    return combine(patients, results, 'and')

# return a combination of the realtime and archival database
def joinResults(array1, array2):
    result_array = array1
    if array2 is not None:
	for value2 in array2:
	    result_array.append(value2)
        
    return result_array
    
# transform the patient condition according to the corresponding database
# E.g. for the cond "sex = male", it transforms to "sex = 'M'" for archival db, or "sex = 0" for realtime db
def transform_Conditions(conditions, db):
    if conditions is None:
        return None

    conditionsArray = []
    if db == "archive":
        for condition in conditions:
            att = condition.getAtt()
            val = condition.getVal()
            if att == "sex" and val == "male":
                condition.setVal('M')
            elif att == "sex" and val == "female":
                condition.setVal('F')
            conditionsArray.append(condition)
    elif db == "realtime":
        for condition in conditions:
            att = condition.getAtt()
            val = condition.getVal()
            if att == "sex" and val == "male":
                condition.setVal(0)
            elif att == "sex" and val == "female":
                condition.setVal(1)
            conditionsArray.append(condition)

    return conditionsArray

# Add the list of terms in query
# E.g. if group = 'sedation', then it returns ('Propofol','Fentanyl',...) to the query
def addTerms(group):
    line = "("

    if group == "sedation":
        path = 'doc/group_sedation_terms.txt'
    elif group == "inotrope":
        path = 'doc/group_inotropes_terms.txt'
    elif group == "antibiotic":
        path = 'doc/group_antibiotic_terms.txt'
    elif group == "thromboembolic prophylaxis":
        path = 'doc/group_thromboembolic_terms.txt'
        
    terms = buildCategoricArray(path)
    line = line + "'" + terms[0] + "'"
    for i in range(1,len(terms)):
        line = line + ",'" + terms[i] + "'"

    line = line + ")"
    return line

# Remove Null values in an array
def removeNull(values):
    new_values = []
    for n in range(0, len(values)):
        try:
            float(str(values[n]))
            new_values.append(values[n])
        except ValueError:
            continue
    
    return new_values

# values in the form [mrn] [value] [chart-time]
# return all numeric value
# use by statistics such as mean and sd
def getValues(values, col = 1):
    new_values = []
    
    for n in range(0, len(values)):
        res = str(values[n]).split(';')
        try:
            float(str(res[col]))
            new_values.append(res[col])
        except ValueError:
            continue
        except IndexError:
            continue
    
    return new_values

#Get the hypothesis testing result based on the keyword (such as t and pval)
def getHypTestResult(keyword):
    try:
        fid = open('test_results.txt','r')
        s = fid.readlines()

        for line in s:
            term = line.split(':')[0].strip()
            if term == keyword:
                return line.split(':')[1].strip()
        return -1

    except IOError, (errno, strerror):
        print "I/O error(%s): %s" % (errno, strerror)

def buildParaIdx(path):
    f = file(path, "r")
    IdxTable = []
    for line in f:
        IdxTable.append(line.strip().lower())
    return IdxTable
        
path = 'doc/interface/med_entities.txt'
med_entities = buildHash(path)
path = 'doc/interface/demo_entities.txt'
demo_entities = buildHash(path)
path = 'doc/category/numeric_idx.txt'
numeric_idx = buildParaIdx(path)
path = 'doc/category/varchar_idx.txt'
varchar_idx = buildParaIdx(path)

# Map the parameters user entered to the correct attributes in the database
def SpecialProcessing(cond_array):
    condition_array = cond_array
    
    for i in range(0, len(condition_array)):
        parameter = condition_array[i].attribute
        value = condition_array[i].value
        if parameter.find("dob") != -1:                   
            time_def = int(value)
            year_int = date.today().year - time_def
            year_str = str(year_int) + "-01-01 00:00:00"
            condition_array[i].value = year_str
            if condition_array[i].comparand == "<":
                condition_array[i].comparand = ">"
            elif condition_array[i].comparand == "<=":
                condition_array[i].comparand = ">="
            elif condition_array[i].comparand == ">":
                condition_array[i].comparand = "<"
            elif condition_array[i].comparand == ">=":
                condition_array[i].comparand = "<="
        elif parameter.find("sex") != -1:
            if value == "female" or value == "f":
                condition_array[i].value = "F"
            elif value == "male" or value == "m":
                condition_array[i].value = "M"
        elif parameter.find("code") != -1:
            condition_array[i].value = condition_array[i].value.upper()
#        print condition_array 
    return condition_array

# Validate and correct the parameters and values based on their data types
def validateCondition(attribute):
    if varchar_idx.count(attribute) <> 0:
        return "varchar"
    elif numeric_idx.count(attribute) <> 0:
        return "numeric"
    return "unknown datatype"

def convertToDatetime(duration):
    cur_datetime = datetime.today()
    start_datetime = cur_datetime - timedelta(duration/24)
    return operator.getslice(str(start_datetime), 0 ,19)

def getLatestVersion():
    cursor = connection.cursor()
    cursor.execute("select max(version_id) from snomed.cdal_version")
    tuple = cursor.fetchone()
#        sys.stderr.write( tuple[0], tuple[1]
    return tuple[0]

def getCurrentTime():
    return strftime("%Y-%m-%d %H:%M:%S")

def transformDOBtoAge(DOB):
    age = date.today().year - int(DOB[0:4])
    return str(age)
    
